using System;
using System.Collections.Generic;
using UnityEngine;

public enum BulletType {

    // 直线
    TYPE1 = 0,
    // 360度直线
    TYPE2,
    // 旋转形
    TYPE3,
    // 正旋+逆向旋转
    TYPE4,
    // 瞄准玩家方向的子弹
    TYPE5,

}

// 用来移动的结构体
// 2D 物体运动的基本参数
public struct MoveParam {

    //　初始角
    internal float angle { get; set; }

    // 旋转角度
    internal float rotation { get; set; }

    // 当前角度
    internal float currentAngle { get; set; }

    // 速度
    internal float speed { get; set; }

    // 加速度
    internal float acceleration { get; set; }

    // 最终方向(附加速度)
    internal Vector3 finalVelocity { get; set; }

}

// 子弹管理
public class BulletManager : MonoBehaviour {

    public GameObject playerGo;
    // 自己的实例
    public static BulletManager instance;

    // 为了方便调用 我们设置匿名方法用来规定封装一下子弹相关的函数
    // 子弹的运动方法(携带参数)
    // 更新子弹的位置 需要两个入参 一个是子弹本身的位置 一个是子弹的运动参数信息
    // ref 实参

    // int i = 0;

    // private void a () {
    //     // i的实参只有在这里才会赋值
    //     i = 1;

    //     b (ref i);

    // }
    // private void b (ref int num) {

    //     // 加了ref关键字的话 我们就能改变 实参的结果
    //     num++;

    //     // i的结果依然是1
    // }

    public delegate void BulletMoveAction (Transform self, ref MoveParam moveParam, Vector3 startPos);

    // public void demoMoveAction(Transform transform, ref MoveParam moveParam) {

    //     // TODO 
    // }

    // 我们用了一个变量 储存了一个方法(匿名方法)
    // public BulletMoveAction demoAction1;

    // // 演示怎么登录匿名方法
    // void ActionDemo() {
    //     this.demoAction1 = demoMoveAction;

    //     MoveParam mp = new MoveParam();
    //     this.demoAction1(this.transform,ref mp);
    // }

    // 设置一个匿名方法用来规定子弹发射
    public delegate void BulletShotAction (Transform origin, GameObject bullet);

    private Dictionary<BulletType, BulletShotAction> bulletShotMap = new Dictionary<BulletType, BulletShotAction> () { };

    void Awake () {

        if (instance == null) {
            // 如果没有自己这个脚本对象，将自身登录
            instance = this;

            bulletShotMap.Add (BulletType.TYPE1, bulletType1);
            bulletShotMap.Add (BulletType.TYPE2, bulletType2);
            bulletShotMap.Add (BulletType.TYPE3, bulletType3);
            bulletShotMap.Add (BulletType.TYPE4, bulletType4);
            bulletShotMap.Add (BulletType.TYPE5, bulletType5);
        }
        //...otherwise...
        else if (instance != this) {
            // 如果已经有这个脚本对象，销毁自己（保持唯一）
            Destroy (gameObject);
        }
    }

    // 开火
    public void Shot (BulletType type, Transform origin, GameObject bullet) {
        bulletShotMap[type] (origin, bullet);
    }

    /// <summary>
    /// 根据角度发射
    /// </summary>
    /// <param name="origin">原点</param>
    /// <param name="bullet">子弹对象</param>
    private void bulletType1 (Transform origin, GameObject bullet) {

        MoveParam moveParam = new MoveParam ();
        // 速度
        moveParam.speed = 0.25f;
        // 发射时的角度

        moveParam.angle = moveParam.currentAngle = 270.0f;
        // 随时间旋转的角度
        moveParam.rotation = 0.0f;
        // 随时间的加速度
        moveParam.acceleration = 0f;

        // Instantiate生成游戏对象的时候 是可以直接获取这个游戏对象
        GameObject bulletGo = Instantiate (bullet, origin.position, origin.rotation) as GameObject;
        // 生成的子弹 获取子弹的脚本组件
        var bulletScript = bulletGo.GetComponent<BulletCustom> ();
        bulletScript.SetParams (
            moveParam,
            BulletActionDirect // 直线弹
        );
        // bulletScript.StartMove ( );
    }

    /// <summary>
    /// 360度全方向发射
    /// </summary>
    /// <param name="origin">原点</param>
    /// <param name="bullet">子弹对象</param>
    private void bulletType2 (Transform origin, GameObject bullet) {

        // 循环一下
        for (var i = 0; i <= 360; i += 30) {
            // 每次增加30度发射一下子弹
            GameObject bulletGo = Instantiate (bullet, origin.position, origin.rotation) as GameObject;
            var bulletScript = bulletGo.GetComponent<BulletCustom> ();
            MoveParam moveParam = new MoveParam ();
            moveParam.speed = 0.5f;
            moveParam.angle = moveParam.currentAngle = i;
            moveParam.rotation = 0.0f;
            // 这里做了一下随时间随更新次数加速的子弹
            moveParam.acceleration = 0.02f;
            bulletScript.SetParams (
                moveParam,
                BulletActionDirect //直线弹
            );
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="origin">原点</param>
    private void bulletType3 (Transform origin, GameObject bullet) {

        for (var i = 0; i <= 360; i += 30) {

            MoveParam moveParam = new MoveParam ();
            moveParam.speed = 0.05f;
            moveParam.angle = moveParam.currentAngle = i;
            moveParam.rotation = 1f;
            moveParam.acceleration = 0.0055f;
            GameObject bulletGo = Instantiate (bullet, origin.position, origin.rotation) as GameObject;
            var bulletScript = bulletGo.GetComponent<BulletCustom> ();
            bulletScript.SetParams (
                moveParam,
                BulletActionCurve
            );
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="origin">原点</param>
    private void bulletType4 (Transform origin, GameObject bullet) {

        for (var i = 0; i <= 360; i += 45) {
            GameObject bulletGo = Instantiate (bullet, origin.position, origin.rotation) as GameObject;

            var bulletScript = bulletGo.GetComponent<BulletCustom> ();
            MoveParam moveParam = new MoveParam ();
            moveParam.speed = 0.015f;
            moveParam.angle = moveParam.currentAngle = i;
            moveParam.rotation = 5f;
            moveParam.acceleration = 0.0025f;
            bulletScript.SetParams (
                moveParam,
                BulletActionCurveAndAccelerate
            );
        }
    }

    // 朝玩家方向发射的子弹
    private void bulletType5 (Transform origin, GameObject bullet) {

        if (playerGo == null) {

            return;
        }

        GameObject bulletGo = Instantiate (bullet, origin.position, origin.rotation) as GameObject;

        var bulletScript = bulletGo.GetComponent<BulletCustom> ();
        MoveParam moveParam = new MoveParam ();
        moveParam.speed = 0.015f;
        moveParam.angle = moveParam.currentAngle = 0;
        moveParam.rotation = 0f;
        moveParam.acceleration = 0.0025f;
        bulletScript.SetParams (
            moveParam,
            BulletActionTargetPlayer
        );

        // 辅助弹 以玩家为方向45度
        GameObject bulletGo1 = Instantiate (bullet, origin.position, origin.rotation) as GameObject;
        var bulletScript1 = bulletGo1.GetComponent<BulletCustom> ();
        MoveParam moveParam1 = new MoveParam ();
        moveParam1.speed = 0.015f;
        moveParam1.angle = moveParam1.currentAngle = +30;
        // moveParam1.rotation = 30f;
        moveParam1.acceleration = 0.0025f;
        bulletScript1.SetParams (
            moveParam1,
            BulletActionTargetPlayerSub
        );

         // 辅助弹 以玩家方向-45度
        GameObject bulletGo2 = Instantiate (bullet, origin.position, origin.rotation) as GameObject;
        var bulletScript2 = bulletGo2.GetComponent<BulletCustom> ();
        MoveParam moveParam2 = new MoveParam ();
        moveParam2.speed = 0.015f;
        moveParam2.angle = moveParam2.currentAngle = -30;
        // moveParam2.rotation = -30f;
        moveParam2.acceleration = 0.0025f;
        bulletScript2.SetParams (
            moveParam2,
            BulletActionTargetPlayerSub
        );

    }

    // 根据角度发射直线弹
    public void BulletActionDirect (Transform bulletTf, ref MoveParam moveParam, Vector3 strat) {

        float posX, posY = 0.0f;

        // Mathf.Deg2Rad = Mathf.PI / 180.0f;
        posX = Mathf.Cos (moveParam.currentAngle * Mathf.Deg2Rad);
        posY = Mathf.Sin (moveParam.currentAngle * Mathf.Deg2Rad);

        // Debug.Log("posX");
        // 坑 会因为精度问题变成科学计数法 而且不完全为0
        // Debug.Log((decimal)posX);
        // Debug.Log("posY");
        // Debug.Log(posY);

        var velocity = new Vector3 (posX, posY, 0).normalized;

        // 向玩家的方向以动
        // bulletRb.velocity = velocity * speed;
        bulletTf.position += velocity * moveParam.speed;

        // 开始加速 (每一帧)
        moveParam.speed = moveParam.speed + moveParam.acceleration;

        /*

            b.x = a.xcos(angle) - a.ysin(angle)
            b.y = a.xsin(angle) + a.ycos(angle)
        */

    }

    //　根据角度发射后可做曲线运动的子弹
    public void BulletActionCurve (Transform bulletTf, ref MoveParam moveParam, Vector3 strat) {

        float posX, posY = 0.0f;

        // Mathf.Deg2Rad = Mathf.PI / 180.0f;
        posX = Mathf.Cos (moveParam.currentAngle * Mathf.Deg2Rad);
        posY = Mathf.Sin (moveParam.currentAngle * Mathf.Deg2Rad);

        var velocity = new Vector3 (posX, posY, 0).normalized;

        bulletTf.position += velocity * moveParam.speed;

        moveParam.speed = moveParam.speed + moveParam.acceleration;

        // 当前角度加算下一次角度的更新量
        moveParam.currentAngle += moveParam.rotation;

    }

    //　根据角度发射后可做曲线运动且变速的子弹
    public void BulletActionCurveAndAccelerate (Transform bulletTf, ref MoveParam moveParam, Vector3 strat) {

        float posX, posY = 0.0f;

        // Mathf.Deg2Rad = Mathf.PI / 180.0f;
        posX = Mathf.Cos (moveParam.currentAngle * Mathf.Deg2Rad);
        posY = Mathf.Sin (moveParam.currentAngle * Mathf.Deg2Rad);

        var velocity = new Vector3 (posX, posY, 0).normalized;

        bulletTf.position += velocity * moveParam.speed;
        moveParam.speed = (moveParam.speed + moveParam.acceleration);

        // Debug.Log ("angle:" + moveParam.angle);
        // Debug.Log ("rotation:" + moveParam.rotation);

        // 正在顺时针旋转 我们的旋转角度 > 0
        bool isClockWise = moveParam.rotation > 0;

        // 如果我们正在顺时针 且 当前角度已经旋转了 360度(一圈)
        if (isClockWise && (moveParam.currentAngle - moveParam.angle >= 360)) {
            // 更改旋转方向
            moveParam.rotation = -moveParam.rotation;
            // 如果正在逆时针 且当前角度 已经旋转回了360度
        } else if (!isClockWise && moveParam.currentAngle - moveParam.angle <= 0) {

            // 更改旋转方向
            moveParam.rotation = -moveParam.rotation;
        }

        moveParam.currentAngle += moveParam.rotation;

    }

    /// <summary>
    /// 瞄准朝向玩家的子弹
    /// </summary>
    /// <param name="bulletTf">子弹当前的位置</param>
    /// <param name="moveParam">移动参数</param>
    /// <param name="StartPostion">发射时候的位置</param>
    public void BulletActionTargetPlayer (Transform bulletTf, ref MoveParam moveParam, Vector3 startPostion) {

        // 方向就是朝向玩家的方向 不需要角度
        // 计算朝向玩家的方向，计算出来后不再更新
        if (moveParam.finalVelocity == Vector3.zero) {
            var dir = playerGo.transform.position - startPostion;

            moveParam.finalVelocity = dir.normalized;

        }

        bulletTf.position += moveParam.finalVelocity * moveParam.speed;
        moveParam.speed = (moveParam.speed + moveParam.acceleration);

    }

    // 以玩家方向的僚机子弹
    public void BulletActionTargetPlayerSub (Transform bulletTf, ref MoveParam moveParam, Vector3 startPostion) {

        // 方向就是朝向玩家的方向 并且偏移n度
        if (moveParam.finalVelocity == Vector3.zero) {
            var dir = playerGo.transform.position - startPostion;

            var newDir = new Vector3 ();


            // 旋转（向量旋转公式）
            // https://wenku.baidu.com/view/1545006da45177232f60a277.html
            // newDir.x = dir.x * Mathf.Cos (moveParam.angle * Mathf.Deg2Rad) + dir.y * Mathf.Sin (moveParam.angle * Mathf.Deg2Rad);
            // newDir.y = dir.y * Mathf.Sin (moveParam.angle * Mathf.Deg2Rad) + dir.y * Mathf.Cos (moveParam.angle * Mathf.Deg2Rad);
            // 用Quaternion.AngleAxis API可以做向量的旋转
            newDir = Quaternion.AngleAxis (moveParam.angle, new Vector3(0,0,1)) * dir;
            moveParam.finalVelocity = newDir.normalized;

        }

        bulletTf.position += moveParam.finalVelocity * moveParam.speed;
        moveParam.speed = (moveParam.speed + moveParam.acceleration);

    }
}